#include "ScanLineRasterizerMT.h"
#include <QThread>
#include <QMutex>
#include <QWaitCondition>

void ScanLineRasterizerMT_FP_Thread::init(int ys, int ye, int ym, int nt, FRAGMENTSHADER shader, ShaderGlobals* glob){
	ystart = ys;
	yend = ye;
	ymod = ym;
	numThreads = nt;

	mShaderGlobals = glob;

	mFragmentShader = shader;
	mFShaderParams.globals = glob;
	mFShaderParams.material = glob->material;

	mCondition.wakeAll();
}

void ScanLineRasterizerMT_FP_Thread::run()
{
	mAlive = true;
	while(mAlive)
	{
		{
			QMutexLocker lock(&mMutex);
			mCondition.wait(&mMutex);
			if (!mAlive)
				break;
			
			for (size_t i = 0; i < face_buffer.size(); i++){
				TransformedFace& tface = (face_buffer)[i];
				if (tface.backface > 0.0){
					drawSpanBetweenEdges(&tface.edges.edges[tface.edges.longEdge], &tface.edges.edges[tface.edges.shortEdge1]);
					drawSpanBetweenEdges(&tface.edges.edges[tface.edges.longEdge], &tface.edges.edges[tface.edges.shortEdge2]);
				}
				if (tface.numVertices > 3 && tface.newBackface > 0.0){
					drawSpanBetweenEdges(&tface.newEdges.edges[tface.newEdges.longEdge], &tface.newEdges.edges[tface.newEdges.shortEdge1]);
					drawSpanBetweenEdges(&tface.newEdges.edges[tface.newEdges.longEdge], &tface.newEdges.edges[tface.newEdges.shortEdge2]);
				}
			}
		}
		mWorkingThreads->deref();
	}
}


inline void setSpan(Fragment* f1, Fragment* f2, Span* e)
{
	if (f1->fragCoord.x <= f2->fragCoord.x){
		e->f1 = f1;
		e->f2 = f2;
	}else{
		e->f1 = f2;
		e->f2 = f1;
	}
	e->diff = e->f2->fragCoord - e->f1->fragCoord;
	for (int i = 0; i < MAX_VARYINGS; i++)
	{
		e->varyingsdiff[i] = e->f2->varyings[i] - e->f1->varyings[i];
	}
}


#define modulo1( x, y) (x % y)
#define modulo2( x, y ) x - (((int)(x/y))*y)
#define modulo(x, y) modulo2(x,y)


void ScanLineRasterizerMT_FP_Thread::drawSpanBetweenEdges(const Edge* e1, const Edge* e2)
{
	if (e1->diff.y == 0.0 || e2->diff.y == 0.0) return ;

	int h = mShaderGlobals->renderTarget->colorBuffer->height;

	Real step1 = 1.0 / e1->diff.y;
	Real factor1 = (e2->f1->fragCoord.y - e1->f1->fragCoord.y) * step1;
	Real step2 = 1.0 / e2->diff.y;
	Real factor2 = 0.0;

	Real y1 = e2->f1->fragCoord.y;
	Real y2 = e2->f2->fragCoord.y;

	if (y1 < 0.5){
		Real f = 0.5 - y1;
		factor1 += f * step1;
		factor2 += f * step2;
		y1 = 0.5;
	}else{
		Real f = 0.5 - (y1 - (int)y1);
		factor1 += f * step1;
		factor2 += f * step2;
	}

	if (y2 > h)
		y2 = h;

	int iy1 = (int)y1;
	int iy2 = (int)y2;
	Real f1, f2;
	
	

	for (int y = iy1; y < iy2; y++)
	{
		if (modulo(y, numThreads) == ymod)
		{
			f1 = factor1 <= 0.0 ? 0.0 : factor1 >= 1.0 ? 1.0 : factor1;
			frag1.fragCoord = e1->diff * f1 + e1->f1->fragCoord;
			for (int i = 0; i < MAX_VARYINGS; i++)
				frag1.varyings[i] = e1->varyingsdiff[i] * f1 + e1->f1->varyings[i];

			f2 = factor2 <= 0.0 ? 0.0 : factor2 >= 1.0 ? 1.0 : factor2;
			frag2.fragCoord = e2->diff * f2 + e2->f1->fragCoord;
			for (int i = 0; i < MAX_VARYINGS; i++)
				frag2.varyings[i] = e2->varyingsdiff[i] * f2 + e2->f1->varyings[i];

			setSpan(&frag1, &frag2, &span);

			drawSpan(span, y);
		}else{
			//std::cout << ystart << " -> " << yend << " : " << y << std::endl;
		}
		factor1 += step1;
		factor2 += step2;
	}
}

void ScanLineRasterizerMT_FP_Thread::drawSpan(Span& span, const int y)
{
	if (span.diff.x == 0) return ;
	

	int w = mShaderGlobals->renderTarget->colorBuffer->width;
	
	Real step = 1.0 / span.diff.x;
	Real factor = 0.0;
	Real x1 = span.f1->fragCoord.x;
	Real x2 = span.f2->fragCoord.x;

	if (x1 < 0.5){
		factor += (0.5 - x1) * step;
		x1 = 0.5;
	}else{
		factor += (0.5 - (x1 - (int)x1)) * step;
	}

	if (x2 > w)
		x2 = w;

	int ix1 = (int)x1;
	int ix2 = (int)x2;
	Real f;
	
	
	DEPTH_IMAGE_FORMAT depth;
	DEPTH_IMAGE_FORMAT* depthBuffer = (w * y + ix1) + (DEPTH_IMAGE_FORMAT*)mShaderGlobals->renderTarget->depthBuffer->imageData;
	//memset((void*)&fragColor, 0, sizeof(fragColor));
	COLOR_IMAGE_FORMAT* colorBuffer = (w * y + ix1) * COLOR_BUFFER_CHANNELS + (COLOR_IMAGE_FORMAT*)mShaderGlobals->renderTarget->colorBuffer->imageData;

	for (int x = ix1; x < ix2; x++){
		f = factor <= 0.0 ? 0.0 : factor >= 1.0 ? 1.0 : factor;

		frag.fragCoord.z = span.diff.z * f + span.f1->fragCoord.z;

		if (frag.fragCoord.z >= -1.0 && frag.fragCoord.z <= 1.0){
			depth = (DEPTH_IMAGE_FORMAT)((frag.fragCoord.z * 0.5 + 0.5) * MAX_DEPTH_VALUE);
			if ( *depthBuffer == 0 || depth <= *depthBuffer){
				frag.fragCoord.w = 1.0 / (span.diff.w * f + span.f1->fragCoord.w);
	
				for (int i = 0; i < MAX_VARYINGS; i++)
					frag.varyings[i] = ((span.varyingsdiff[i] * f) + span.f1->varyings[i]) * frag.fragCoord.w;

				mFShaderParams.fragment = &frag;
				mFragmentShader(&mFShaderParams);

				colorBuffer[2] = mFShaderParams.out.x <= 0.0 ? 0 : mFShaderParams.out.x >= 1.0 ? MAX_COLOR_VALUE : (COLOR_IMAGE_FORMAT)(mFShaderParams.out.x * MAX_COLOR_VALUE);
				colorBuffer[1] = mFShaderParams.out.y <= 0.0 ? 0 : mFShaderParams.out.y >= 1.0 ? MAX_COLOR_VALUE : (COLOR_IMAGE_FORMAT)(mFShaderParams.out.y * MAX_COLOR_VALUE);
				colorBuffer[0] = mFShaderParams.out.z <= 0.0 ? 0 : mFShaderParams.out.z >= 1.0 ? MAX_COLOR_VALUE : (COLOR_IMAGE_FORMAT)(mFShaderParams.out.z * MAX_COLOR_VALUE);

				*depthBuffer = depth;
			}
		}
		factor += step;
		depthBuffer++;
		colorBuffer += COLOR_BUFFER_CHANNELS;
	}

}